home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / rcs.exe / CACHE.DOC < prev    next >
Encoding:
Text File  |  1992-09-01  |  12.8 KB  |  317 lines

  1.                     A Simple Reentrant Cache System
  2.                              Version 1.0
  3.                        by Philip J. Erdelsky
  4.                        CompuServe 75746,3411
  5.                  InterNet 75746.3411@compuserve.com
  6.  
  7.                         September 1, 1992
  8.  
  9.                PUBLIC DOMAIN -- NO RESTRICTIONS ON USE
  10.  
  11. 1. Introduction
  12. ---------------
  13.  
  14. Many applications that use disks tend to read and write the same sectors
  15. repeatedly. A technique called caching can make such applications run much
  16. faster. With this technique, frequently used sectors are read into memory
  17. buffers when the application begins. The application then reads and writes the
  18. memory buffers. When the application terminates, the buffers are written to
  19. the disk. Since reading and writing memory buffers is much faster than reading
  20. or writing disk sectors, the application can run much faster. There are some
  21. problems with caching. For example, it can be difficult to determine which
  22. sectors will be used frequently. However, the technique is widely used because
  23. the improvement is often quite substantial.
  24.  
  25. This cache system is a simple one, designed to be placed between the Reentrant
  26. DOS-Compatible File System (RDCF) and a disk driver.
  27.  
  28. A single cache can be used for two or more drives, as long as they all have
  29. the same sector size. The cache system is not reentrant with respect to
  30. different drives in this case, so access to it must be serialized.
  31.  
  32. The cache system can handle as many separate caches as available memory
  33. permits. It is fully reentrant with respect to different caches. However,
  34. separate caches may not access the same drive.
  35.  
  36. The cache system calls the standard C functions malloc() and free() when a
  37. cache is being initialized or freed, so reentrancy of those functions is
  38. required at those times. However, it does no memory allocation or deallocation
  39. at other times.
  40.  
  41. The number of sectors in the cache and the size of each sector are determined
  42. at run time and need not be the same for all caches. The only limit to the
  43. number of sectors in the cache is available memory. A cached sector need not
  44. correspond to a single sector on disk, but it must be treated as such by
  45. functions that call and are called by the cache system.
  46.  
  47. Each cache can handle up to 32,768 drives, numbered from 0 to 32,767, and up to
  48. 65,536 sectors on each drive, numbered from 0 to 65,535. The use of larger
  49. numbers will produce numeric overflow and catastrophic failure. Drive numbers
  50. and sector numbers need not be consecutive.
  51.  
  52. No cache system can be optimal in every conceivable case, no matter how
  53. cleverly it is programmed. This cache system uses one of the simplest methods,
  54. and it is apparently adequate for some purposes. You are welcome to make your
  55. own improvements, but you must assume responsibility for the results if you
  56. make a mistake.
  57.  
  58. In this cache system, each cached sector may exist in any of three states:
  59.  
  60.      (1) An EMPTY sector bears no relation to any disk sector.
  61.  
  62.      (2) A CLEAN sector contains the same data as the corresponding disk
  63.          sector.
  64.  
  65.      (3) A DIRTY sector contains data which is to be written to the
  66.          corresponding disk sector but has not yet been written.
  67.  
  68. The cache system attempts to keep each sector in the cache as long as it can,
  69. and it uses the least-recently-used criterion when cached sectors must be
  70. reused. The process is explained in greater detail in Section 4.
  71.  
  72. The source code for the cache system consists of the file CACHE.C and the
  73. header file CACHE.H. The latter should be #included in any source file that
  74. calls on the cache system, because it contains prototypes for all cache
  75. functions and other necessary information.
  76.  
  77. The cache system calls on the following standard C functions:
  78.  
  79.      malloc()
  80.      free()
  81.      memcpy()
  82.  
  83. To prevent identifier conflicts, all publicly defined identifiers in the cache
  84. system begin with the characters "cache", "CACHE" or "_CACHE".
  85.  
  86.  
  87. 2. How the Cache Reads and Writes a Sector
  88. ------------------------------------------
  89.  
  90. The cache system does not read or write the disk directly. You must provide it
  91. with a pointer to a function that you have written for your implementation. The
  92. cache system then calls this function whenever it needs to read or write a disk
  93. sector. The format of the function call is as follows:
  94.  
  95.      e = (*drive_access)(write, drive, sector, buffer);
  96.  
  97.      int write;        nonzero (true) for a write operation; zero (false)
  98.                        for a read operation
  99.  
  100.      unsigned drive;   drive to be read or written
  101.  
  102.      unsigned sector;  sector to be read or written
  103.  
  104.      void *buffer;     address of memory buffer to receive or supply data
  105.  
  106.      unsigned e;       zero for a successful operation, or an
  107.                        implementation-defined nonzero error code
  108.  
  109. When the cache receives a nonzero value from this function, it aborts the cache
  110. operation and returns the value as its own functional value. The cache control
  111. block contains the drive and sector numbers of the offending operation in the
  112. members error_drive and error_sector, respectively. This is especially helpful
  113. for diagnostic purpose, because the error almost always occurs in a sector
  114. other than the one currently being accessed by the application.
  115.  
  116.  
  117. 3. Creating and Initializing a Cache
  118. ------------------------------------
  119.  
  120. The following function call creates and initializes a cache:
  121.  
  122.      q = cache_initialize(drive_access, number_of_sectors, sector_size);
  123.  
  124.      unsigned (*drive_access)();  pointer to function called by cache
  125.                                   to read or write a sector
  126.  
  127.      unsigned number_of_sectors;  number of sectors to be stored in cache
  128.                                   (must be at least 1)
  129.  
  130.      unsigned sector_size;        number of bytes in a sector (1-65,535)
  131.  
  132.      struct cache *q;             pointer to cache control block, or NULL
  133.                                   if there was insufficient memory
  134.  
  135. The function calls on malloc() to allocate memory for the cache storage. If
  136. malloc() returns NULL at any point in the process, the function calls free()
  137. to release any memory that it has allocated and returns a NULL pointer.
  138. Otherwise, it returns a pointer that can be passed to other cache functions.
  139. It marks all sectors in the cache as empty.
  140.  
  141. A sector size near the upper limit of 65,535 may cause numeric overflow and
  142. catastrophic failure if the implementation does not permit allocation of single
  143. objects larger than 65,535 bytes.
  144.  
  145.  
  146. 4. Reading or Writing Through the Cache
  147. ---------------------------------------
  148.  
  149. The heart of the cache system is the following function call:
  150.  
  151.      e = cache_access(q, write, drive, sector, buffer);
  152.  
  153.      struct cache *q;  pointer to cache control block received from
  154.                        cache_initialize()
  155.  
  156.      int write;        nonzero (true) for a write operation; zero (false)
  157.                        for a read operation
  158.  
  159.      unsigned drive;   drive to be read or written
  160.  
  161.      unsigned sector;  sector to be read or written
  162.  
  163.      void *buffer;     address of memory buffer to receive or supply data
  164.  
  165.      unsigned e;       zero for a successful operation, or an
  166.                        implementation-defined nonzero error code
  167.  
  168. This function writes or reads the specified sector to or from the specified
  169. drive, using the cache as follows:
  170.  
  171.     (1) If the specified sector is not already in the cache, a cache sector
  172.         is assigned to it as follows:
  173.  
  174.          (a) If there are still empty sectors in the cache, one of them is
  175.              assigned to the specified sector.
  176.  
  177.          (b) If there are no empty sectors, but there are some clean ones,
  178.              the least-recently used clean sector is marked as empty and
  179.              assigned to the specified sector.
  180.  
  181.          (c) In other cases, the least-recently used dirty sector is written
  182.              to disk, marked as empty, and assigned to the specified sector.
  183.  
  184.      (2) When reading,
  185.  
  186.          (a) If the sector is empty, it is read from the disk and marked as
  187.              clean.
  188.  
  189.          (b) The sector is then copied from the cache to the calling program's
  190.              buffer.
  191.  
  192.      (3) When writing, the calling program's buffer is copied to the cache and
  193.          the sector is marked as dirty. IT IS NOT WRITTEN TO DISK.
  194.  
  195.      (4) The sector is marked as the most recently used.
  196.  
  197. If an error is detected on an actual disk access, this function aborts the
  198. operation, puts the drive and sector numbers of the offending sector into
  199. q->error_drive and q->error sector, and returns the implementation-defined
  200. nonzero error code passed to it by (*drive_access)(). Otherwise, it returns
  201. zero.
  202.  
  203.  
  204. 5. Flushing the Cache
  205. ---------------------
  206.  
  207. Before a disk can be changed or removed, the dirty sectors must be written out.
  208. The following function call does that, either for a specified drive or for all
  209. drives:
  210.  
  211.      e = cache_flush(q, drive);
  212.  
  213.      struct cache *q;  pointer to cache control block received from
  214.                        cache_initialize()
  215.  
  216.      int drive;        drive to be flushed; if drive<0, all drives are
  217.                        flushed
  218.  
  219.      unsigned e;       zero for a successful operation, or an
  220.                        implementation-defined nonzero error code
  221.  
  222. If the drive number is nonnegative, this function writes all dirty sectors to
  223. the specified drive and marks them as clean. Sectors on other drives are not
  224. affected. If the drive number is negative, it writes all dirty sectors to all
  225. drives and marks them as clean.
  226.  
  227. The sectors are not written in any specified order, and their usage order is
  228. not changed.
  229.  
  230. If an error is detected while writing a sector, this function aborts the
  231. operation, puts the drive and sector numbers of the offending sector into
  232. q->error_drive and q->error sector, and returns the implementation-defined
  233. nonzero error code passed to it by (*drive_access)(). Otherwise, it returns
  234. zero.
  235.  
  236. This function is implemented as a macro which generates a call on the function
  237. cache_flush_and_or_clear().
  238.  
  239.  
  240. 6. Clearing the Cache
  241. ---------------------
  242.  
  243. After a disk is changed, the cached sectors on that drive become invalid. The
  244. following function call informs the cache system of that fact:
  245.  
  246.      cache_clear(q, drive);
  247.  
  248.      struct cache *q;  pointer to cache control block received from
  249.                        cache_initialize()
  250.  
  251.      int drive;        drive to be cleared; if drive<0, all drives are
  252.                        cleared
  253.  
  254. If the drive number is nonnegative, this function marks all cached sectors on
  255. the specified drive as empty. Sectors on other drives are not affected. If the
  256. drive number is negative, it marks all cached sectors on all drives as empty.
  257.  
  258. THIS FUNCTION DOES NOT WRITE THE CACHED SECTORS TO DISK, EVEN IF THEY ARE
  259. DIRTY.
  260.  
  261. This function is implemented as a macro which generates a call on the function
  262. cache_flush_and_or_clear().
  263.  
  264.  
  265. 7. Flushing and Clearing the Cache
  266. ----------------------------------
  267.  
  268. Before a disk can be changed and removed, the dirty sectors must be written
  269. out, and the cached sectors on the drive must be marked as invalid. The
  270. following function call does that, either for a specified drive or for all
  271. drives:
  272.  
  273.      e = cache_flush_and_clear(q, drive);
  274.  
  275.      struct cache *q;  pointer to cache control block received from
  276.                        cache_initialize()
  277.  
  278.      int drive;        drive to be flushed and cleared; if drive<0, all
  279.                        drives are flushed and cleared
  280.  
  281.      unsigned e;       zero for a successful operation, or an
  282.                        implementation-defined nonzero error code
  283.  
  284. If the drive number is nonnegative, this function first finds all sectors
  285. assigned to the specified drive. It writes the dirty sectors to the specified
  286. drive and then marks both clean and dirty sectors as empty. Sectors on other
  287. drives are not affected. If the drive number is negative, it does the same
  288. thing to all sectors on all drives.
  289.  
  290. The sectors are not written in any specified order.
  291.  
  292. If an error is detected while writing a sector, this function aborts the
  293. operation, puts the drive and sector numbers of the offending sector into
  294. q->error_drive and q->error sector, and returns the implementation-defined
  295. nonzero error code passed to it by (*drive_access)(). Otherwise, it returns
  296. zero.
  297.  
  298. This function is equivalent to calls on cache_flush() and cache_clear(), but it
  299. is more efficient because it makes only a single pass through the cache.
  300.  
  301. This function is implemented as a macro which generates a call on the function
  302. cache_flush_and_or_clear().
  303.  
  304.  
  305. 8. Freeing the Cache
  306. --------------------
  307.  
  308. The following function calls free() to release all memory allocated for a
  309. cache:
  310.  
  311.      cache_free(q);
  312.  
  313.      struct cache *q;  pointer to cache control block received from
  314.                        cache_initialize()
  315.  
  316.  
  317.